﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices; //调用WINDOWS API函数时要用到
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Drawing;
using System.Media;

namespace Qiguan

{
    //todo:
    //      1、检测战斗是否结束，若没有就让脚本线程多等一会儿
    //      2、出征前检查船只血量并自动维修
    //      3、新增3个战斗结束检测点维、6个船只点位、1个维修按钮点位。
    //      4、Dictionary的Add方法重构为覆盖保存模式
    //      5、夜战检测          
    //      6、UI交互面板
    static class Program
    {
        //和UI面板有关的变量
        public static int zhayu_num = 0;//脚本计数
        public static string info = "";//控制台信息
        public static string formation = "Formation5";//当前阵型

        static int staxWidth = System.Windows.Forms.SystemInformation.PrimaryMonitorSize.Width;
        static int yHeight = System.Windows.Forms.SystemInformation.PrimaryMonitorSize.Height;
        static int tmouse = 100;//鼠标定位延时
        static int tl = 6000;//长延时
        static int tm = 3000;//中延时
        static int ts = 2000;//短延时

        static MainForm mainForm;
        static MyBin myBin = new MyBin();
        static Dictionary<string, SoundPlayer> sDic = new Dictionary<string, SoundPlayer>();
        static Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
        static Bitmap bmp_mouse = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

        static bool isWritting = false;//打字模式
        static bool isGoing = false;//脚本线程已启动
        static bool end = false;//终止jio本
        static bool[] needFix = new bool[7]{ false, false, false, false, false, false, false};//0下下标对应舰队内是否有非健康状态的船只，1~6号下标对应每艘船的健康状态
        

        //键盘事件响应方法
        private static void hook_KeyDown(object sender, KeyEventArgs e)
        {

            //"~"键的键值：Keys.Oemtilde  MouseHelper.SetCursorPos(Control.MousePosition.X, Control.MousePosition.Y);

            //切换打字模式
            if (e.KeyValue == (int)Keys.F1)
            {
                if (isWritting == true) { isWritting = false; } else { isWritting = true; }
            }

            //打字模式下不出发jio本
            if (isWritting == true) {return;}

            //触发jio本
            if (e.KeyValue == (int)Keys.T) {
                if (isGoing == true) { show_info("脚本不能重复启动！"); return; }
                else{ isGoing = true; }
                //compareWithPixel(myBin.dic["Mid"]);
                Thread thread = new Thread(myThread);
                thread.Start();
                return;
            }
            /**中键 Mid
             * 出征键 Go
             * 开始战斗键 Battle
             * 反潜阵型键 Formation1 ~ Formation5
             * 快速修理键 Fix
             * 船位按钮（及血条监测) Ship1 ~ Ship6
             * 回港键 Return 
             * 夜战检测点3个 NightYes NightNo Night
             * **/
            switch (e.KeyValue)
            {
                case (int)Keys.M :
                    myBin.add("Mid", getPosition());
                    break;
                case (int)Keys.G:
                    myBin.add("Go", getPosition());
                    break;
                case (int)Keys.B:
                    myBin.add("Battle", getPosition());
                    break;
                case (int)Keys.F:
                    myBin.add("Fix", getPosition());
                    break;
                case (int)Keys.R:
                    myBin.add("Return", getPosition());
                    break;
                case (int)Keys.F2:
                    save();
                    break;
                case (int)Keys.Q:
                    end = true;
                    break;
                case (int)Keys.D:
                    //Debug
                    //click(myBin.dic["Fix"]);
                    break;
                //阵型点位1-5
                case (int)Keys.NumPad1:
                    myBin.add("Formation1", getPosition());
                    break;
                case (int)Keys.NumPad2:
                    myBin.add("Formation2", getPosition());
                    break;
                case (int)Keys.NumPad3:
                    myBin.add("Formation3", getPosition());
                    break;
                case (int)Keys.NumPad4:
                    myBin.add("Formation4", getPosition());
                    break;
                case (int)Keys.NumPad5:
                    myBin.add("Formation5", getPosition());
                    break;

                //战斗结束检测点位
                case (int)Keys.D7:
                    myBin.add("Finished1", getPixelAndPosition());
                    break;
                case (int)Keys.D8:
                    myBin.add("Finished2", getPixelAndPosition());
                    break;
                case (int)Keys.D9:
                    myBin.add("Finished3", getPixelAndPosition());
                    break;

                //船位点位
                case (int)Keys.D1:
                    myBin.add("Ship1", getPixelAndPosition());
                    break;
                case (int)Keys.D2:
                    myBin.add("Ship2", getPixelAndPosition());
                    break;
                case (int)Keys.D3:
                    myBin.add("Ship3", getPixelAndPosition());
                    break;
                case (int)Keys.D4:
                    myBin.add("Ship4", getPixelAndPosition());
                    break;
                case (int)Keys.D5:
                    myBin.add("Ship5", getPixelAndPosition());
                    break;
                case (int)Keys.D6:
                    myBin.add("Ship6", getPixelAndPosition());
                    break;
                //夜战检测点
                case (int)Keys.NumPad6:
                    myBin.add("NightYes", getPixelAndPosition());
                    break;
                case (int)Keys.NumPad7:
                    myBin.add("NightNo", getPixelAndPosition());
                    break;
                case (int)Keys.NumPad8:
                    myBin.add("Night", getPixelAndPosition());
                    break;

            }

        }

        //获取当前屏幕截图 格式1920*1080
        public static void getScreenImg()
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(0, 0, 0, 0, new Size(1920, 1080));
            }
            //bmp.Save("test.bmp");
        }
        
        //获取当前鼠标位置及像素值
        public static PixelAndPosition getPixelAndPosition()
        {
            using (Graphics g = Graphics.FromImage(bmp_mouse))
            {
                g.CopyFromScreen(0, 0, 0, 0, new Size(1920, 1080));
            }
            Color c = bmp_mouse.GetPixel(Control.MousePosition.X, Control.MousePosition.Y);
            PixelAndPosition pp = new PixelAndPosition(Control.MousePosition.X, Control.MousePosition.Y, c.ToString());
            return pp;
        }

        //获取当前鼠标位置
        public static PixelAndPosition getPosition()
        {
            PixelAndPosition pp = new PixelAndPosition(Control.MousePosition.X, Control.MousePosition.Y);
            return pp;
        }

        //UI控制台缓冲区添加信息
        public static void show_info(string s)
        {
            lock (info)
            {
                info += "\n";
                info += s;
            }
        }

        //与截图中指定坐标的像素值比较
        public static bool compareWithPixel(PixelAndPosition pp)
        {
            
            Color c2 = bmp.GetPixel(pp.x, pp.y);

            if (pp.p.Equals(c2.ToString()))
            { show_info("图色检测：true！");return true; }
            else
            { show_info("图色检测：false！"); return false; }

        }

        //存储持久化数据
        public static void save()
        {
            string filePath = "myBin.bin";
            using (FileStream fs = new FileStream(filePath, FileMode.Create))
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(fs, myBin);
            }
            show_info("执行了一次点位数据持久化！");
        }

        //读取持久化数据
        public static void read()
        {

            string filePath = "myBin.bin";

            try
            {
                using (FileStream fs = new FileStream(filePath, FileMode.Open))
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    myBin = bf.Deserialize(fs) as MyBin;
                }

                //显示持久化数据
                show_info("持久化数据为：");
                foreach (string key in myBin.dic.Keys)
                {
                    PixelAndPosition pp = myBin.dic[key];
                    show_info("键名：" + key + " , 键值：" + pp.x + " " + pp.y + " " + pp.p );
                }
                
            }
            catch (Exception e)
            {
                Console.WriteLine("异常出现："+e);
            }

        }

        //模拟鼠标点击指定位置
        public static void click(PixelAndPosition pp)
        {
            MouseHelper.SetCursorPos(pp.x, pp.y);
            Thread.Sleep(tmouse);
            MouseHelper.mouse_event(MouseHelper.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            Thread.Sleep(tmouse);
            MouseHelper.mouse_event(MouseHelper.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        }

        //判断战斗是否结束(未结束也可能是因为要夜战)
        public static bool isBattleFinished()
        {
            getScreenImg();
            if (!compareWithPixel(myBin.dic["Finished1"])) { return false; }
            if (!compareWithPixel(myBin.dic["Finished2"])) { return false; }
            if (!compareWithPixel(myBin.dic["Finished3"])) { return false; }
            else { return true; }
        }

        //判断战斗有夜战
        public static bool isNightBattle()
        {
            getScreenImg();
            if (!compareWithPixel(myBin.dic["NightYes"])) { return false; }
            if (!compareWithPixel(myBin.dic["NightNo"])) { return false; }
            if (!compareWithPixel(myBin.dic["Night"])) { return false; }
            else { return true; }
        }

        //检查是否有船只需要修理，若则标记
        public static bool needFixed()
        {
            int i = 1;
            getScreenImg();
            for(; i<=6;i++)
            {
               if(compareWithPixel(myBin.dic["Ship" + i]))
                {
                    show_info(i+"号船只处于健康状态。");
                }
                else
                {
                    show_info(i + "号船只处于非健康状态，奖进行修理。");
                    needFix[0] = true;
                    needFix[i] = true;
                }
            }
            return needFix[0];
            
        }

        //按标记进行修理，最后复位needFix数组
        public static void fixShips()
        {
            Thread.Sleep(ts);
            click(myBin.dic["Fix"]);
            Thread.Sleep(ts);

            int i = 1;
            for(; i<=6;i++)
            {
                if (needFix[i] == true)
                {
                    if (end == true) { break; }
                    click(myBin.dic["Ship" + i]);
                    needFix[i] = false;
                    show_info(i + "号船只完成修理。");
                    Thread.Sleep(ts);
                }

            }
            needFix[0] = false;
        }

        //判断上次敲击回港按钮是否失效,失效则再点击
        public static void checkFault()
        {
            //true 代表故障
            getScreenImg();
            if(compareWithPixel(myBin.dic["NightYes"]))
            {
                click(myBin.dic["Return"]);//回港，保险点击
                show_info("捕捉到一次回港键失灵！！！！！");
                Thread.Sleep(tl);

                getScreenImg();
                if (compareWithPixel(myBin.dic["NightYes"]))
                {
                    show_info("异常：连续2次点击回港键失灵，退出脚本！");
                    sDic["fault"].Play();
                    end = true;
                }
            }
        }

        //脚本线程
        public static void myThread()
        {
            /**中键 Mid
             * 出征键 Go
             * 开始战斗键 Battle
             * 反潜阵型键 Formation1 ~ Formation5
             * 快速修理键 Fix
             * 船位按钮（及血条监测) Ship1 ~ Ship6
             * 回港键 Return
             * 战斗结束检测点位 Finished1 ~ Finished3
             *顺序：
             *    敲击中键 进入地图战备界面
             *    5S后 （可能要多点时间）
             *    
             *    船只损害管理检查（记录损害6个变量）
             *    IF YES[
             *     点击快速修理键
             *     2S后
             *     修理指定船只
             *     2S后  循环
             *     损害变量复位
             *    ]
             *    
             *    2S后 额外
             *    敲击出征键
             *    4S后
             *    敲击中键 跳过选路动画
             *    6S后
             *    敲击中键 跳过索敌动画
             *    2S后
             *    敲击开始战斗键（是否可与出征键公用？），开始选阵型
             *    2S后
             *    敲击反潜阵键（是否可与出征键公用？），开始战斗
             *    30S后 （战斗时间）
             *    敲击中键 跳过战斗评级界面
             *    2S后
             *    敲击中键 跳过经验收益界面
             *    2S后
             *    敲击中键 跳过船只打捞界面
             *    2S后
             *    敲击回港键 回港
             *    5S后 （可能要多点时间）
             *    
             *    **/

            zhayu_num = 0;
            while (end == false)
            {
                //click(myBin.dic[""]);
                //Thread.Sleep();
                zhayu_num++;
                
                Console.WriteLine("第"+ zhayu_num +"次炸鱼开始了！");
                sDic["script_start"].Play();

                if (end == true)break;
                click(myBin.dic["Mid"]);//进入地图
                Thread.Sleep(tm+1000);

                if (end == true) break;
                if (needFixed())//检查舰队是否有船只需要修理
                {
                    sDic["fix"].Play();
                    fixShips();//修理船只
                }
                else { sDic["no_fix"].Play(); }
                

                if (end == true) break;
                click(myBin.dic["Go"]);//出征
                Thread.Sleep(tm+1000);

                if (end == true) break;
                click(myBin.dic["Mid"]);//跳过选路动画
                Thread.Sleep(tm+1000);

                if (end == true) break;
                click(myBin.dic["Mid"]);//跳过索敌动画
                Thread.Sleep(tm);//可能4000

                if (end == true) break;
                click(myBin.dic["Battle"]);//开始战斗
                Thread.Sleep(ts);

                if (end == true) break;
                click(myBin.dic[formation]);//选择阵型
                Thread.Sleep(1000);//为了保险，进行二次点击，2000ms也可能故障
                if (end == true) break;
                click(myBin.dic[formation]);//选择阵型，保险点击
                sDic["battle_start"].Play();

                Thread.Sleep(20000);//战斗时间，预设为20s

                if (end == true) break;
                int delay_num = 0;//记录延时次数
                while (!isBattleFinished())//检测是否战斗结束
                {
                    if (delay_num > 8)//异常战斗等待检测
                    {
                        show_info("出现异常！战斗等待超时！退出脚本！");
                        sDic["out_time"].Play();
                        end = true; break;
                    }

                    if (isNightBattle())//判断是否有夜战
                    {
                        delay_num = 0 ;//进入夜战后重置下延时次数
                        click(myBin.dic["NightYes"]);//进入夜战
                        show_info("进入夜战！");
                        sDic["night_battle"].Play();
                        Thread.Sleep(10000);//夜战延时10s
                    }
                    else//昼战或夜战的延时等待
                    {
                        delay_num++;
                        show_info("检测,战斗延时6秒。已累计" + delay_num + "次。");
                        sDic["is_finished"].Play();
                        Thread.Sleep(tl);
                        if (end == true) break;
                    }
                }

                Thread.Sleep(ts);//检测到战斗结束前缓冲2s
                if (end == true) break;
                show_info("战斗结束！");
                sDic["battle_over"].Play();


                if (end == true) break;
                click(myBin.dic["Mid"]);//跳过战斗评级界面
                Thread.Sleep(ts);

                if (end == true) break;
                click(myBin.dic["Mid"]);//跳过经验收益界面
                Thread.Sleep(ts);

                if (end == true) break;
                click(myBin.dic["Mid"]);//跳过船只打捞界面
                Thread.Sleep(4000);//3000ms延时有几率点不上

                if (end == true) break;
                click(myBin.dic["Return"]);//回港
                sDic["back"].Play();
                Thread.Sleep(tl);
                //回港键失灵保险
                if (end == true) break;
                checkFault();
                if (end == true) break;

                show_info("第" + zhayu_num + "次炸鱼结束了！");
                
            }
            isGoing = false;
            end = false;
            show_info("炸鱼jio本停止了！已经累计炸鱼"+zhayu_num+ "次！(最后一次可能未完成)");
        }

        //资源初始化
        public static void init()
        {
            //加载音频文件
            string[] s = new string[11] { "back", "battle_start", "battle_over", "is_finished", "night_battle", "out_time", "script_over", "script_start","fix","no_fix","fault" };
            int i = 0;
            for(; i<s.Length;i++)
            {
                SoundPlayer player = new SoundPlayer();
                player.SoundLocation = s[i] + ".wav";
                player.Load();
                sDic.Add(s[i], player);
            }
           
            //读取点位数据
            read();

            show_info("软件初始化完成！");
        }

        [STAThread]
        static void Main()

        {
            //车轮子初始化
            KeyboardHook k_hook = new KeyboardHook();
            k_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//钩住键按下 
            k_hook.Start();//安装键盘钩子
            
            //我的初始化
            init();

            //UI初始化
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            mainForm = new MainForm();
            
            Application.Run(mainForm);
        }

        //模拟键盘点击的方法，这个比sendKey方法更快响应
        // 参数：虚拟键值，一般为0，这里是整数类型（0为按下 2为释放），这里是整数类型 一般情况下设成为0
        [DllImport("user32.dll")]
        public static extern void keybd_event(byte bVk,byte bScan,int dwFlags,int dwExtraInfo );
    }

    
    // 车轮子：鼠标钩子
    class MouseHelper
    {
        [System.Runtime.InteropServices.DllImport("user32")]
        public static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
        //移动鼠标 
        public const int MOUSEEVENTF_MOVE = 0x0001;
        //模拟鼠标左键按下 
        public const int MOUSEEVENTF_LEFTDOWN = 0x0002;
        //模拟鼠标左键抬起 
        public const int MOUSEEVENTF_LEFTUP = 0x0004;
        //模拟鼠标右键按下 
        public const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
        //模拟鼠标右键抬起 
        public const int MOUSEEVENTF_RIGHTUP = 0x0010;
        //模拟鼠标中键按下 
        public const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
        //模拟鼠标中键抬起 
        public const int MOUSEEVENTF_MIDDLEUP = 0x0040;
        //标示是否采用绝对坐标 
        public const int MOUSEEVENTF_ABSOLUTE = 0x8000;
        [DllImport("user32.dll")]
        public static extern bool SetCursorPos(int X, int Y);
    }

    
    // 车轮子：键盘钩子
    class KeyboardHook
    {
        public event KeyEventHandler KeyDownEvent;
        public event KeyPressEventHandler KeyPressEvent;
        public event KeyEventHandler KeyUpEvent;

        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
        static int hKeyboardHook = 0; //声明键盘钩子处理的初始值
                                      //值在Microsoft SDK的Winuser.h里查询
                                      // http://www.bianceng.cn/Programming/csharp/201410/45484.htm
        public const int WH_KEYBOARD_LL = 13;   //线程键盘钩子监听鼠标消息设为2，全局键盘监听鼠标消息设为13
        HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型
                                        //键盘结构
        [StructLayout(LayoutKind.Sequential)]
        public class KeyboardHookStruct
        {
            public int vkCode;  //定一个虚拟键码。该代码必须有一个价值的范围1至254
            public int scanCode; // 指定的硬件扫描码的关键
            public int flags;  // 键标志
            public int time; // 指定的时间戳记的这个讯息
            public int dwExtraInfo; // 指定额外信息相关的信息
        }
        //使用此功能，安装了一个钩子
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);


        //调用此函数卸载钩子
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);


        //使用此功能，通过信息钩子继续下一个钩子
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        // 取得当前线程编号（线程钩子需要用到）
        [DllImport("kernel32.dll")]
        static extern int GetCurrentThreadId();

        //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        public void Start()
        {
            // 安装键盘钩子
            if (hKeyboardHook == 0)
            {
                KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
                //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
                //************************************
                //键盘线程钩子
                //SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),
                //键盘全局钩子,需要引用空间(using System.Reflection;)
                //SetWindowsHookEx( 13,MouseHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
                //
                //关于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函数将钩子加入到钩子链表中，说明一下四个参数：
                //idHook 钩子类型，即确定钩子监听何种消息，上面的代码中设为2，即监听键盘消息并且是线程钩子，如果是全局钩子监听键盘消息应设为13，
                //线程钩子监听鼠标消息设为7，全局钩子监听鼠标消息设为14。lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的
                //线程的标识，lpfn必须指向DLL中的钩子子程。 除此以外，lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址，当钩子钩到任何
                //消息后便调用这个函数。hInstance应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程，而且子
                //程代码位于当前进程，hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。threaded 与安装的钩子子程相关联的线程的标识符
                //如果为0，钩子子程与所有的线程关联，即为全局钩子
                //************************************
                //如果SetWindowsHookEx失败
                if (hKeyboardHook == 0)
                {
                    Stop();
                    throw new Exception("安装键盘钩子失败");
                }
            }
        }
        public void Stop()
        {
            bool retKeyboard = true;


            if (hKeyboardHook != 0)
            {
                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
                hKeyboardHook = 0;
            }

            if (!(retKeyboard)) throw new Exception("卸载钩子失败！");
        }
        //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符
        [DllImport("user32")]
        public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。
                                         int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键，如果是（不压）
                                         byte[] lpbKeyState, // [in] 指针，以256字节数组，包含当前键盘的状态。每个元素（字节）的数组包含状态的一个关键。如果高阶位的字节是一套，关键是下跌（按下）。在低比特，如果设置表明，关键是对切换。在此功能，只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。
                                         byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。
                                         int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.

        //获取按键的状态
        [DllImport("user32")]
        public static extern int GetKeyboardState(byte[] pbKeyState);


        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern short GetKeyState(int vKey);

        private const int WM_KEYDOWN = 0x100;//KEYDOWN
        private const int WM_KEYUP = 0x101;//KEYUP
        private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN
        private const int WM_SYSKEYUP = 0x105;//SYSKEYUP

        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            // 侦听键盘事件
            if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
            {
                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                // raise KeyDown
                if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
                {
                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
                    KeyEventArgs e = new KeyEventArgs(keyData);
                    KeyDownEvent(this, e);
                }

                //键盘按下
                if (KeyPressEvent != null && wParam == WM_KEYDOWN)
                {
                    byte[] keyState = new byte[256];
                    GetKeyboardState(keyState);

                    byte[] inBuffer = new byte[2];
                    if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
                    {
                        KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
                        KeyPressEvent(this, e);
                    }
                }

                // 键盘抬起
                if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
                {
                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
                    KeyEventArgs e = new KeyEventArgs(keyData);
                    KeyUpEvent(this, e);
                }

            }
            //如果返回1，则结束消息，这个消息到此为止，不再传递。
            //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递，也就是传给消息真正的接受者
            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
        }
        ~KeyboardHook()
        {
            Stop();
        }
    }

    

    [Serializable]
    class MyBin
    {

        public Dictionary<string, PixelAndPosition> dic = new Dictionary<string, PixelAndPosition>();
        public Dictionary<string, PixelAndPosition> Dic
        {
            get { return dic; }
            set { dic = value; }
        }

        public void add(string s,PixelAndPosition pp)
        {
            if (dic.ContainsKey(s))
            {
                dic[s] = pp;
            }
            else
            {
                dic.Add(s, pp);
            }
        }
    }

    [Serializable]
    class PixelAndPosition
    {
        public int x = 0;
        public int y = 0;
        public string p = "";

        public int X
        {
            get { return x; }
            set { x = value; }
        }

        public int Y
        {
            get { return y; }
            set { y = value; }
        }

        public string P
        {
            get { return p; }
            set { p = value; }
        }

        public PixelAndPosition(int x, int y, string p)
        {
            this.x = x;
            this.y = y;
            this.p = p;
        }

        public PixelAndPosition(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

    }




}
